/*
 * MIT License
 *
 * Copyright (c) 2025 Mark·虎
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 */

package com.hsxxherp.key.common.util;


import com.hsxxherp.key.common.annotation.TreeChildren;
import com.hsxxherp.key.common.annotation.TreeId;
import com.hsxxherp.key.common.annotation.TreeParentId;

import java.lang.reflect.Field;
import java.util.*;

/**
 * 数表处理工具
 *
 * @author Mark·虎克 (2486970363@qq.com)
 * @since 2024-1-25
 */
public class TreeStructureUtil {
    private static final int CODE_LENGTH = 4;

    /**
     * 生成内码
     *
     * @param parentObject   父对象
     * @param siblingObjects 同级对象列表
     * @return 生成的内码
     * @throws NoSuchFieldException   如果对象中缺少必要的字段
     * @throws IllegalAccessException 如果反射访问字段时出现异常
     */
    public static String generateInnerCode(Object parentObject, List<Object> siblingObjects) throws NoSuchFieldException, IllegalAccessException {
        String parentInnerCode = getInnerCode(parentObject);
        int maxSuffix = 0;
        for (Object sibling : siblingObjects) {
            String siblingInnerCode = getInnerCode(sibling);
            if (siblingInnerCode != null && siblingInnerCode.startsWith(parentInnerCode)) {
                String suffixStr = siblingInnerCode.substring(parentInnerCode.length());
                if (suffixStr.length() == CODE_LENGTH) {
                    try {
                        int suffix = Integer.parseInt(suffixStr);
                        if (suffix > maxSuffix) {
                            maxSuffix = suffix;
                        }
                    } catch (NumberFormatException e) {
                        // 忽略格式错误的内码
                    }
                }
            }
        }
        String newSuffix = String.format("%0" + CODE_LENGTH + "d", maxSuffix + 1);
        return parentInnerCode + newSuffix;
    }

    /**
     * 树表内码重算
     *
     * @param rootObject 根对象
     * @throws NoSuchFieldException   如果对象中缺少必要的字段
     * @throws IllegalAccessException 如果反射访问字段时出现异常
     */
    public static void recalculateInnerCodes(Object rootObject) throws NoSuchFieldException, IllegalAccessException {
        if (rootObject == null) {
            return;
        }
        if (getParentId(rootObject) == null) {
            setInnerCode(rootObject, generateInnerCode(null, null));
        }
        List<Object> children = getChildren(rootObject);
        if (children != null) {
            for (Object child : children) {
                setInnerCode(child, generateInnerCode(rootObject, children));
                recalculateInnerCodes(child);
            }
        }
    }

    /**
     * 通用平表解析为树表
     *
     * @param flatList 平表数据列表
     * @param <T>      对象类型
     * @return 树表结构的根对象列表
     * @throws IllegalArgumentException 如果类中缺少必要的注解
     * @throws IllegalAccessException   如果反射访问字段时出现异常
     */
    public static <T> List<T> convertToTree(List<T> flatList) throws IllegalArgumentException, IllegalAccessException {
        if (flatList == null || flatList.isEmpty()) {
            return Collections.emptyList();
        }

        Map<Long, T> map = new HashMap<>();
        List<T> roots = new ArrayList<>();
        Class<?> clazz = flatList.get(0).getClass();

        Field idField = findFieldByAnnotation(clazz, TreeId.class);
        Field parentIdField = findFieldByAnnotation(clazz, TreeParentId.class);
        Field childrenField = findFieldByAnnotation(clazz, TreeChildren.class);

        if (idField == null || parentIdField == null || childrenField == null) {
            throw new IllegalArgumentException("Missing required annotations in the class");
        }

        idField.setAccessible(true);
        parentIdField.setAccessible(true);
        childrenField.setAccessible(true);

        // 先将所有对象放入 Map 中
        for (T item : flatList) {
            Long id = (Long) idField.get(item);
            map.put(id, item);
        }

        // 构建树结构
        for (T item : flatList) {
            Long parentId = (Long) parentIdField.get(item);
            if (parentId == null) {
                roots.add(item);
            } else {
                T parent = map.get(parentId);
                if (parent != null) {
                    List<T> children = (List<T>) childrenField.get(parent);
                    if (children == null) {
                        children = new ArrayList<>();
                        childrenField.set(parent, children);
                    }
                    children.add(item);
                }
            }
        }

        return roots;
    }

    /**
     * 根据注解查找类中的字段
     *
     * @param clazz           类
     * @param annotationClass 注解类
     * @return 找到的字段，若未找到则返回 null
     */
    private static Field findFieldByAnnotation(Class<?> clazz, Class<?> annotationClass) {
        for (Field field : clazz.getDeclaredFields()) {
            if (field.isAnnotationPresent(annotationClass.asSubclass(java.lang.annotation.Annotation.class))) {
                return field;
            }
        }
        return null;
    }

    /**
     * 获取对象的内码
     *
     * @param object 对象
     * @return 内码
     * @throws NoSuchFieldException   如果对象中缺少 innercode 字段
     * @throws IllegalAccessException 如果反射访问字段时出现异常
     */
    private static String getInnerCode(Object object) throws NoSuchFieldException, IllegalAccessException {
        if (object == null) {
            return "";
        }
        Field innercodeField = object.getClass().getDeclaredField("innercode");
        innercodeField.setAccessible(true);
        return (String) innercodeField.get(object);
    }

    /**
     * 设置对象的内码
     *
     * @param object    对象
     * @param innercode 内码
     * @throws NoSuchFieldException   如果对象中缺少 innercode 字段
     * @throws IllegalAccessException 如果反射访问字段时出现异常
     */
    private static void setInnerCode(Object object, String innercode) throws NoSuchFieldException, IllegalAccessException {
        if (object == null) {
            return;
        }
        Field innercodeField = object.getClass().getDeclaredField("innercode");
        innercodeField.setAccessible(true);
        innercodeField.set(object, innercode);
    }

    /**
     * 获取对象的父 ID
     *
     * @param object 对象
     * @return 父 ID
     * @throws NoSuchFieldException   如果对象中缺少 parentId 字段
     * @throws IllegalAccessException 如果反射访问字段时出现异常
     */
    private static Long getParentId(Object object) throws NoSuchFieldException, IllegalAccessException {
        if (object == null) {
            return null;
        }
        Field parentIdField = object.getClass().getDeclaredField("parentId");
        parentIdField.setAccessible(true);
        return (Long) parentIdField.get(object);
    }

    /**
     * 获取对象的子列表
     *
     * @param object 对象
     * @return 子列表
     * @throws NoSuchFieldException   如果对象中缺少 children 字段
     * @throws IllegalAccessException 如果反射访问字段时出现异常
     */
    @SuppressWarnings("unchecked")
    private static List<Object> getChildren(Object object) throws NoSuchFieldException, IllegalAccessException {
        if (object == null) {
            return null;
        }
        Field childrenField = object.getClass().getDeclaredField("children");
        childrenField.setAccessible(true);
        return (List<Object>) childrenField.get(object);
    }
}